package org.volicy.mysql.schema.interceptor;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.volicy.mysql.schema.configuration.MysqlSchemaProperties;
import org.volicy.mysql.schema.configuration.SchemaContextHolder;
import org.volicy.mysql.schema.util.SqlParserUtil;

import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

/**
 * @Author: volicy.xu
 * @Date: 2021/9/15
 */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class MysqlSchemaInterceptor implements Interceptor {
    private static final String DELEGATE_BOUNDSQL_SQL = "delegate.boundSql.sql";
    @Autowired
    private MysqlSchemaProperties mysqlSchemaProperties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = realTarget(invocation.getTarget());
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        Object value = metaObject.getValue(DELEGATE_BOUNDSQL_SQL);
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

        Map<String, String> mapping = mysqlSchemaProperties.getMapping();
        String schema = SchemaContextHolder.getSchemaType();//mysqlSchemaProperties.getDataSourceMapping().get(SchemaContextHolder.getSchemaType());

        if (!CollectionUtils.isEmpty(mapping) && !StringUtils.hasText(schema)) {
            for (Map.Entry<String, String> mapp : mapping.entrySet()) {
                if (mappedStatement.getId().indexOf(mapp.getKey()) > -1) {
                    schema = mapp.getValue();
                }
            }
        }
        if (StringUtils.hasText(schema)) {
            switch (mappedStatement.getSqlCommandType()) {
                case SELECT:
                case INSERT:
                case DELETE:
                case UPDATE: {
                    String sql = SqlParserUtil.sqlParser(value.toString(), schema);
                    metaObject.setValue(DELEGATE_BOUNDSQL_SQL, sql);
                }
                break;
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    public <T> T realTarget(Object target) {
        if (Proxy.isProxyClass(target.getClass())) {
            MetaObject metaObject = SystemMetaObject.forObject(target);
            return realTarget(metaObject.getValue("h.target"));
        }
        return (T) target;
    }
}
